package acme.serviceproviders.webservices;

import java.util.*;

import javax.jws.*;
import org.hibernate.Session;
import acme.serviceproviders.*;
import acme.serviceproviders.data.*;
import acme.serviceproviders.data.utility.*;

@WebService
public class FlightService {

	private final long AIRLINE_ACCOUNT = 111111111;

	@WebMethod
	public FlightItinerary[] getFlights(Date startDate, Date returnDate,
			String startCity, String targetCity)
			throws DateIncorrectException {
		if (startDate.after(returnDate))
			throw new DateIncorrectException(
					"Start Date is later then return Date");

		if (startDate.before(Calendar.getInstance().getTime()))
			throw new DateIncorrectException("Start Date has already passed");

		Session session = HibernateUtility.getSessionFactory().openSession();

		try {
			List<Plane> startPlanes = session
					.createQuery(
							"from Plane where startCity = :startCity AND targetCity = :targetCity")
					.setString("startCity", startCity).setString("targetCity",
							targetCity).list();

			List<Plane> returnPlanes = session
					.createQuery(
							"from Plane where targetCity = :startCity AND startCity = :targetCity")
					.setString("startCity", startCity).setString("targetCity",
							targetCity).list();

			List<FlightItinerary> itineraries = generateItineraries(
					startPlanes, returnPlanes, startDate, returnDate);
			FlightItinerary[] result = new FlightItinerary[itineraries.size()];
			itineraries.toArray(result);

			return result;
		} finally {
			session.close();
		}
	}

	@WebMethod
	public String bookFlight(int planeId, Date travelDate, long accountNumber)
			throws DateIncorrectException, PlaneNotExistsException
			{
		if (travelDate.before(Calendar.getInstance().getTime()))
			throw new DateIncorrectException("Travel Date has already passed");

		Session session = HibernateUtility.getSessionFactory().openSession();

		try {
			Plane plane = (Plane) session.createQuery(
					"from Plane where planeId = :planeId").setInteger(
					"planeId", planeId).uniqueResult();

			if (plane == null)
				throw new PlaneNotExistsException("Plane not found");

			FlightBooking booking = new FlightBooking();
			booking.setBookingId(UUID.randomUUID().toString());
			booking.setTravelDate(travelDate);
			booking.setPlane(plane);
			booking.setBookingFee(plane.getTripcost());

			chargeBookingCost(accountNumber, plane.getTripcost());
			session.save(booking);

			session.flush();

			return booking.getBookingId();
		} finally {
			session.close();
		}
	}

	@WebMethod
	public float cancelFlight(String bookingId, long accountNumber)
			throws BookingNotFoundException {
		Session session = HibernateUtility.getSessionFactory().openSession();

		try {
			FlightBooking booking = (FlightBooking) session.createQuery(
					"from FlightBooking where bookingId = ?").setString(0,
					bookingId).uniqueResult();

			if (booking == null)
				throw new BookingNotFoundException("Booking not found");

			float returnAmount = calculateReturnAmount(booking.getTravelDate(),
					booking.getBookingFee());
			session.delete(booking);
			returnBookingCost(accountNumber, returnAmount);

			session.flush();
			session.close();

			return returnAmount;
		} finally {
			session.close();
		}
	}

	@WebMethod
	public float calculateCancellationFee(String bookingId)
			throws BookingNotFoundException {
		Session session = HibernateUtility.getSessionFactory().openSession();

		try {
			FlightBooking booking = (FlightBooking) session.createQuery(
					"from FlightBooking where bookingId = ?").setString(0,
					bookingId).uniqueResult();

			if (booking == null)
				throw new BookingNotFoundException("Booking not found");

			return booking.getBookingFee()
					- calculateReturnAmount(booking.getTravelDate(), booking
							.getBookingFee());
		} finally {
			session.close();
		}
	}
	
	@WebMethod
	FlightBooking getBookingDetails(String bookingId) throws BookingNotFoundException
	{
		Session session = HibernateUtility.getSessionFactory().openSession();

		try {
			FlightBooking booking = (FlightBooking) session.createQuery(
					"from FlightBooking where bookingId = ?").setString(0,
					bookingId).uniqueResult();

			if (booking == null)
				throw new BookingNotFoundException("Booking not found");

			return booking;
		} 
		finally {
			session.close();
		}		
	}

	private ArrayList<FlightItinerary> generateItineraries(
			List<Plane> startPlaneList, List<Plane> returnPlaneList,
			Date startDate, Date returnDate) {
		ArrayList<FlightItinerary> result = new ArrayList<FlightItinerary>();

		for (Plane startPlane : startPlaneList)
			for (Plane returnPlane : returnPlaneList) {
				// TODO: set datetime correction logic
				// startPlane.setDepartureTime(ChangeToCorrectDate(startDate,
				// startPlane.getDepartureTime()));
				// startPlane.setArrivalTime(ChangeToCorrectDate(startDate,
				// startPlane.getArrivalTime()));
				// returnPlane.setDepartureTime(ChangeToCorrectDate(returnDate,
				// returnPlane.getDepartureTime()));
				// returnPlane.setArrivalTime(ChangeToCorrectDate(returnDate,
				// returnPlane.getArrivalTime()));
				FlightItinerary itinerary = new FlightItinerary();
				itinerary.setStartPlane(startPlane);
				itinerary.setReturnPlane(returnPlane);
				result.add(itinerary);
			}

		return result;
	}

	private Date ChangeToCorrectDate(Date date, Date time) {
		// TODO: implement correction logic

		return date;
	}

	private void chargeBookingCost(long accountNumber, float bookingCost) {
		// TODO Implement Bank communication logic
		BankWSClient.TransferMoney(accountNumber, AIRLINE_ACCOUNT, bookingCost);
	}

	private float calculateReturnAmount(Date travelDate, float bookingFee) {
		// TODO Apply Cancellation policy
		return bookingFee;
	}

	private void returnBookingCost(long accountNumber, float returnAmount) {
		// TODO Implement Bank communication logic
		BankWSClient
				.TransferMoney(AIRLINE_ACCOUNT, accountNumber, returnAmount);
	}
}
